package com.esun.dao.das;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.db.nosql.mongo.MongoFactory;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.esun.dao.eo.PageEo;
import com.esun.dao.eo.SortEo;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.Sorts;
import com.mongodb.client.model.Updates;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Repository;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;


/**
 * 基础数据业务
 *
 * @author John.xiao
 * @date 2021/7/25 22:27
 */
@Repository
@Data
@Slf4j
public abstract class BaseDas<T> {

    @Value("${spring.data.mongodb.database}")
    private String dbname;

    private String collectionName;


    public void init(){
        setCollectionName("base-collection");
    }

    public MongoCollection<Document> getCollection(String collectionName) {

        String group = "master";
        MongoDatabase database = MongoFactory.getDS(group).getDb(dbname);
        return database.getCollection(collectionName);
    }

    /**
     * 按传入参数进行条件查询，全为null时查询全部
     *
     * @param entity   查询实体
     * @param sortList 排序列表
     * @param page     分页信息
     * @return 查询结果
     */
    public List<T> query(T entity, List<SortEo> sortList, PageEo page) {
        init();
        MongoCollection<Document> collection = getCollection(collectionName);
        Field[] fields = ReflectUtil.getFields(entity.getClass());
        List<Bson> bsonList = new ArrayList<>();
        List<Bson> sortBsonList = new ArrayList<>();
        for (Field field : fields) {
            String fieldName = field.getName();
            Object value = ReflectUtil.getFieldValue(entity, fieldName);
            if (ObjectUtil.isNotEmpty(value)) {
                bsonList.add(Filters.eq(fieldName, value));
            }
        }
        if (CollectionUtils.isNotEmpty(sortList)){
            for (SortEo sortParam : sortList) {
                if (sortParam.getAscending()) {
                    sortBsonList.add(Sorts.ascending(sortParam.getSortParam()));
                } else {
                    sortBsonList.add(Sorts.descending(sortParam.getSortParam()));
                }
            }
        }
        Bson conditions = new Document();
        if (CollectionUtils.isNotEmpty(bsonList)){
            conditions = Filters.and(bsonList);
        }
        List<T> resultList = new ArrayList<>();
        FindIterable<Document> result;
        if (ObjectUtil.isNotNull(page)) {
            int startIndex= (page.getPageIndex()-1) *page.getPageSize();
            result = collection.find(conditions).sort(Sorts.orderBy(sortBsonList)).skip(startIndex).batchSize(page.getPageSize()).limit(page.getPageSize());
        } else {
            result = collection.find(conditions).sort(Sorts.orderBy(sortBsonList));
        }
        for (Document o : result) {
            o.remove("_id");
            resultList.add((T) JSONArray.parseObject(JSON.toJSONString(o),entity.getClass()));
        }
        return resultList;
    }

    /**
     * 单条插入
     *
     * @param entity 实体
     */
    public void insert(T entity) {
        init();
        MongoCollection<Document> collection = getCollection(collectionName);
        Document document = Document.parse(JSON.toJSONString(entity));
        log.info("=================== insert  document is:{}", document);
        collection.insertOne(document);
    }

    /**
     * 批量插入
     *
     * @param entityList 值列表
     * @return 执行结果
     */
    public boolean insertBatch(List<T> entityList) {
        init();
        MongoCollection<Document> collection = getCollection(collectionName);
        List<Document> documentList = new ArrayList<>();
        for (T entity : entityList) {
            Document document = Document.parse(JSON.toJSONString(entity));
            documentList.add(document);
        }
        collection.insertMany(documentList);
        return true;
    }

    /**
     * 批量更新
     *
     * @param query 查询实体
     * @param upset 更新实体
     * @return 执行结果
     */
    public long update(T query, T upset) {
        init();
        MongoCollection<Document> collection = getCollection(collectionName);
        long updateNum = 0;
        try {
            Document filter = getDocumentByEntity(query);
            Document updateSet = getUpdateSet(upset);
            UpdateResult result = collection.updateMany(filter,  Updates.combine(updateSet));
            updateNum = result.getModifiedCount();
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
            log.error("updateBatch error {}", e.getMessage());
        }
        return updateNum;
    }

    /**
     * 批量删除
     *
     * @param entity 实体
     */
    public long delete(T entity){
        init();
        MongoCollection<Document> collection = getCollection(collectionName);
        if (entity == null) {
            log.error("deleteByEntity error: update entity is null");
            return 0;
        }
        try {
            Document query = getDocumentByEntity(entity);
            if (query.size() <= 0) {
                log.error("deleteByEntity error: entity doesn't have any properties");
            }
            DeleteResult re = collection.deleteMany(query);
            return re.getDeletedCount();
        } catch (Exception e) {
            log.error("deleteByEntity error {} class:{}", e.getMessage(), entity.getClass().getName());
        }
        return 0;
    }

    private Document getDocumentByEntity(T entity) throws IllegalAccessException, InvocationTargetException {
        Document query = new Document();
        if (entity == null) {
            return query;
        }
        Field[] fields = ReflectUtil.getFields(entity.getClass());
        for (Field f : fields) {
            if (StringUtils.equals("serialVersionUID", f.getName())) {
                continue;
            }
            f.setAccessible(true);
            Object obj = f.get(entity);
            if (null == obj) {
                continue;
            }
            query.append(f.getName(), obj);
        }
        return query;
    }

    private Document getUpdateSet(T entity) throws IllegalAccessException, InvocationTargetException {
        Document set = getDocumentByEntity(entity);
        set.remove("_id");
        set = new Document("$set", set);
        return set;
    }
}
